﻿//copyright © kaveh.shahbazian@gmail.com 2013 (see license.txt)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

using System.Diagnostics;
using System.Threading;
using System.IO;

namespace Funtastic
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            if (!PrepareFsi())
            {
                MessageBox.Show(@"F# Interactive (fsi.exe) can't be found, check path in 'Funtastic.exe.config'", "Wrong Path", MessageBoxButton.OK, MessageBoxImage.Error);
                this.Close();
                return;
            }

            _openFileDialog = new System.Windows.Forms.OpenFileDialog();
            _openFileDialog.Filter = "F# script files (*.fsx)|*.fsx|All Files (*.*)|*.*";
            _openFileDialog.FileOk += new System.ComponentModel.CancelEventHandler((sender, e) =>
            {
                if (_scriptChanged)
                {
                    var question = MessageBox.Show("There is unsaved work.\r\nOpen anyway?", "Unsaved Work", MessageBoxButton.YesNo, MessageBoxImage.Warning);

                    if (question == MessageBoxResult.No)
                    {
                        e.Cancel = true;
                        return;
                    }
                }

                if (System.IO.File.Exists(_openFileDialog.FileName))
                {
                    using (var reader = new System.IO.StreamReader(_openFileDialog.FileName, true))
                        this.TextBoxScript.Text = reader.ReadToEnd();

                    _scriptChanged = false;
                }
            });

            _saveFileDialog = new System.Windows.Forms.SaveFileDialog();
            _saveFileDialog.Filter = "F# script files (*.fsx)|*.fsx|All Files (*.*)|*.*";
            _saveFileDialog.FileOk += new System.ComponentModel.CancelEventHandler((sender, e) =>
            {
                SaveToSelectedFile();
            });

            this.TextBoxScript.Text = "// select the code that you want to send to F# interactive then press alt+enter";
            _scriptChanged = false;
        }

        void SaveToSelectedFile()
        {
            if (!string.IsNullOrWhiteSpace(_saveFileDialog.FileName))
            {
                using (var writer = new System.IO.StreamWriter(_saveFileDialog.FileName, false, Encoding.UTF8))
                    writer.Write(this.TextBoxScript.Text);

                _scriptChanged = false;
            }
        }

        System.Windows.Forms.OpenFileDialog _openFileDialog;
        System.Windows.Forms.SaveFileDialog _saveFileDialog;

        #region fsi tools

        static readonly string space4 = new string(' ', 4);
        Process _fsi;

        bool PrepareFsi()
        {
            ShutdownFsi();

            if (!System.IO.File.Exists(AppSettings.FsiPath))
            {
                return false;
            }

            var fsiStart = new ProcessStartInfo()
            {
                CreateNoWindow = true,
                UseShellExecute = false,
                FileName = AppSettings.FsiPath,
                RedirectStandardError = true,
                RedirectStandardInput = true,
                RedirectStandardOutput = true,
                ErrorDialog = false
            };

            _fsi = new Process();
            _fsi.StartInfo = fsiStart;
            _fsi.EnableRaisingEvents = true;

            _fsi.Start();

            Func<string, string> cleanUp = (str) =>
            {
                return System.Text.RegularExpressions.Regex.Replace(str, @"^([\s]*>[\s]*)+", string.Empty) + Environment.NewLine;
            };

            _fsi.ErrorDataReceived += (s, e) =>
            {
                Thread.Sleep(100);

                Action act = () => this.TextBoxOutput.AppendText(e.Data + Environment.NewLine);
                this.Dispatcher.Invoke(act, System.Windows.Threading.DispatcherPriority.Input);
            };

            _fsi.OutputDataReceived += (s, e) =>
            {
                Action act = () => this.TextBoxOutput.AppendText(cleanUp(e.Data));
                this.Dispatcher.Invoke(act, System.Windows.Threading.DispatcherPriority.Input);
            };

            _fsi.BeginErrorReadLine();
            _fsi.BeginOutputReadLine();

            return true;
        }

        void ShutdownFsi()
        {
            if (_fsi != null)
                try
                {
                    _fsi.StandardInput.WriteLine("#quit;;");
                    _fsi.Close();
                }
                catch { }

            try
            {
                if (_fsi != null)
                    if (!_fsi.HasExited && !_fsi.WaitForExit(1000))
                    {
                        _fsi.Kill();
                    }
            }
            catch { }
        }

        void SendSelectedCodeToFsi()
        {
            _fsi.StandardInput.Write(this.TextBoxScript.SelectedText + ";;\n\r\r");
            _fsi.StandardInput.Flush();
        }

        #endregion

        #region handling alt+enter & tab

        Key? _previousKey;
        Key? _currentKey;

        void TextBoxScript_PreviewKeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Tab)
            {
                e.Handled = true;
                var flag = this.TextBoxScript.SelectionStart;
                this.TextBoxScript.Text = this.TextBoxScript.Text.Insert(flag, space4);
                this.TextBoxScript.SelectionStart = flag + 4;
            }

            _previousKey = _currentKey;
            _currentKey = e.SystemKey;

            if (this._previousKey.HasValue && this._currentKey.HasValue)
            {
                var combination = this._previousKey.Value | this._currentKey.Value;

                var st1 = Key.LeftAlt | Key.Return;
                var st2 = Key.RightAlt | Key.Return;

                if (combination == st1 || combination == st2)
                {
                    _previousKey = null;
                    _currentKey = null;

                    e.Handled = true;
                    SendSelectedCodeToFsi();
                }

                this._previousKey = null;
            }
        }

        #endregion

        void TextBoxOutput_TextChanged(object sender, TextChangedEventArgs e)
        {
            this.TextBoxOutput.ScrollToEnd();
        }

        bool _scriptChanged = false;
        void TextBoxScript_TextChanged(object sender, TextChangedEventArgs e)
        {
            _scriptChanged = true;
        }

        void Window_Loaded(object sender, RoutedEventArgs e) { }

        void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            if (_scriptChanged)
            {
                var question = MessageBox.Show("There is unsaved work.\r\nExit anyway?", "Unsaved Work", MessageBoxButton.YesNo, MessageBoxImage.Warning);

                if (question == MessageBoxResult.No)
                {
                    e.Cancel = true;
                    return;
                }
            }

            if (_openFileDialog != null)
                _openFileDialog.Dispose();

            if (_saveFileDialog != null)
                _saveFileDialog.Dispose();

            ShutdownFsi();
        }

        void MenuItem_Exit_Click(object sender, RoutedEventArgs e)
        {
            this.Close();
        }

        void MenuItem_Open_Click(object sender, RoutedEventArgs e)
        {
            _openFileDialog.ShowDialog();
        }

        void MenuItem_Save_Click(object sender, RoutedEventArgs e)
        {
            var cond1 = System.IO.File.Exists(_saveFileDialog.FileName);
            var cond2 = System.IO.File.Exists(_openFileDialog.FileName);

            if (cond1 || cond2)
            {
                if (cond2)
                    _saveFileDialog.FileName = _openFileDialog.FileName;

                SaveToSelectedFile();
            }
            else
            {
                _saveFileDialog.ShowDialog();
                SaveToSelectedFile();
            }
        }

        void MenuItem_Copy_Click(object sender, RoutedEventArgs e)
        {
            this.TextBoxScript.Copy();
        }

        void MenuItem_Cut_Click(object sender, RoutedEventArgs e)
        {
            this.TextBoxScript.Cut();
        }

        void MenuItem_Paste_Click(object sender, RoutedEventArgs e)
        {
            this.TextBoxScript.Paste();
        }

        void MenuItem_SaveAs_Click(object sender, RoutedEventArgs e)
        {
            _saveFileDialog.ShowDialog();
            SaveToSelectedFile();
        }
    }
}
